﻿using System;
using System.ComponentModel;
using OnlineGameClient.Service;
using OnlineGameLibrary.Game;
using OnlineGameLibrary.MessageEx;
using OnlineGameLibrary.Contract;
using System.ServiceModel.Channels;
using Microsoft.Practices.Composite.Presentation.Commands;
using System.Collections.Generic;
using System.Windows.Threading;
using System.Linq;
using System.Windows.Data;
using OnlineGameClient.View;

namespace OnlineGameClient.ViewModel
{
    public class GameRoomViewModel : INotifyPropertyChanged
    {
        object currentgameroomlock = new object();
        Dispatcher _dispatcher;

        public GameImporter GameImporter { set; get; }
        public GameBase CurrentGame { set; get; }

        IModalDialogService _modeldialgservice = new ModalDialogService();
        IDeploymentCatalogService _deploymentcatalogservice = new DeploymentCatalogService();

        public GameRoomViewModel(Dispatcher dispatcher, GameServiceClientProxy serviceproxy)
        {
            GameImporter = new GameImporter();
            _dispatcher = dispatcher;

            //initialize property changed handler
            this.PropertyChanged += GameRoomViewModel_PropertyChanged;

            this.GameServiceClientProxy = serviceproxy;

            InitializeCommand();
        }

        void GameRoomViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "CurrentGameRoom":
                    CreateGameRoomCommand.RaiseCanExecuteChanged();
                    AttendGameRoomCommand.RaiseCanExecuteChanged();
                    LeaveGameRoomCommand.RaiseCanExecuteChanged();
                    StartGameCommand.RaiseCanExecuteChanged();
                    BackToGameRoomCommand.RaiseCanExecuteChanged();
                    if (CurrentGameRoom != null)
                    {
                        CurrentGameRoom.Players.CollectionChanged += (Players_CollectionChanged);
                    }
                    break;
            }
        }

        void Players_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            StartGameCommand.RaiseCanExecuteChanged();
        }

        GameServiceClientProxy _gameserviceproxy;
        internal GameServiceClientProxy GameServiceClientProxy
        {
            get
            {
                return _gameserviceproxy;
            }
            set
            {
                if (_gameserviceproxy != null)
                {
                    _gameserviceproxy.MessageReceived -=
                        _gameserviceproxy_MessageReceivedFromServer;
                    _gameserviceproxy.ChannelClosed -= _gameserviceproxy_ChannelClosed;
                }
                _gameserviceproxy = value;
                _gameserviceproxy.MessageReceived +=
                    _gameserviceproxy_MessageReceivedFromServer;
                _gameserviceproxy.ChannelClosed += _gameserviceproxy_ChannelClosed;
            }
        }

        void _gameserviceproxy_ChannelClosed(object sender, EventArgs e)
        {
            var loginw = new LoginWindow();
            loginw.DataContext = this;
            loginw.Show();
        }

        #region notify properties
        public event PropertyChangedEventHandler PropertyChanged;

        void onPropertyChanged(string propertyname)
        {
            if (PropertyChanged != null)
            {
                _dispatcher.BeginInvoke(delegate
                {
                    PropertyChanged(this,
                        new PropertyChangedEventArgs(propertyname));
                });
            }
        }

        Player _loginplayer;
        public Player LoginPlayer
        {
            set
            {
                if (value != _loginplayer)
                {
                    _loginplayer = value;
                    onPropertyChanged("LoginPlayer");
                }
            }
            get
            {
                return _loginplayer;
            }
        }

        double _downloadprogress;
        public double DownloadProgress
        {
            set
            {
                if (value != _downloadprogress)
                {
                    _downloadprogress = value;
                    onPropertyChanged("DownloadProgress");
                }
            }
            get
            {
                return _downloadprogress;
            }
        }

        PagedCollectionView _gameroomlist;
        public PagedCollectionView GameRoomList
        {
            set
            {
                if (value != _gameroomlist)
                {
                    _gameroomlist = value;
                    onPropertyChanged("GameRoomList");
                }
            }
            get
            {
                return _gameroomlist;
            }
        }

        List<GameInfo> _gameinfolist;
        public List<GameInfo> GameInfoList
        {
            set
            {
                if (value != _gameinfolist)
                {
                    _gameinfolist = value;
                    onPropertyChanged("GameInfoList");
                }
            }
            get
            {
                return _gameinfolist;
            }
        }

        GameRoom _curgameroom;
        public GameRoom CurrentGameRoom
        {
            set
            {
                if (value != _curgameroom)
                {
                    _curgameroom = value;
                    onPropertyChanged("CurrentGameRoom");
                }
            }
            get
            {
                return _curgameroom;
            }
        }
        #endregion

        #region message handling
        // message received.
        void _gameserviceproxy_MessageReceivedFromServer(object sender, MessageEventArgs e)
        {
            try
            {
                var scope = e.Message.GetScope();

                if (scope == GameRoomMessageDefines.Namespace)
                {
                    var action = e.Message.GetAction();
                    if (action == GameRoomMessageDefines.ROOM_PLAYER_JOIN.Action)
                    {
                        if (this.CurrentGameRoom != null)
                        {
                            var joinplayer = GameRoomMessageDefines.ROOM_PLAYER_JOIN.GetMessage(e.Message).MessageBodyContent as Player;

                            if (joinplayer.PlayerID != LoginPlayer.PlayerID)
                            {
                                _dispatcher.BeginInvoke(delegate
                                {
                                    this.CurrentGameRoom.Players.Add(joinplayer);
                                });
                            }
                        }
                    }

                    else if (action == GameRoomMessageDefines.ROOM_PLAYER_UPDATE.Action)
                    {
                        var updateplayer = GameRoomMessageDefines.ROOM_PLAYER_UPDATE.GetMessage(e.Message).MessageBodyContent as Player;
                        var tarplayer = this.CurrentGameRoom.Players.First(p => p.PlayerID == updateplayer.PlayerID);

                        // update player properties here.
                        _dispatcher.BeginInvoke(delegate
                        {

                            ////////////////////
                            // temp fix....
                            if (tarplayer == null)
                            {
                                System.Windows.MessageBox.Show("Here we got a async action sequence issue, we'll try to fix it in future.\n Currently, you need leave the room and re join game.\nsorry for that.");
                            }


                            tarplayer.PlayerState = updateplayer.PlayerState;
                        });
                    }

                    else if (action == GameRoomMessageDefines.ROOM_PLAYER_LEAVE.Action)
                    {
                        var leaveplayer = GameRoomMessageDefines.ROOM_PLAYER_LEAVE.GetMessage(e.Message).MessageBodyContent as Player;
                        var tarplayer = this.CurrentGameRoom.Players.First(p => p.PlayerID == leaveplayer.PlayerID);
                        if (this.CurrentGame != null)
                        {
                            this.CurrentGame.onPlayerLeave(leaveplayer.PlayerID);
                        }
                        _dispatcher.BeginInvoke(delegate
                        {
                            this.CurrentGameRoom.Players.Remove(tarplayer);
                        });
                    }

                    else if (action == GameRoomMessageDefines.ROOM_CONFIG_UPDATE.Action)
                    {
                        var updateconfig = GameRoomMessageDefines.ROOM_CONFIG_UPDATE.GetMessage(e.Message).MessageBodyContent as GameConfig;
                        _dispatcher.BeginInvoke(delegate
                        {
                            foreach (var kvpair in updateconfig.ConfigDict)
                            {
                                this.CurrentGameRoom.GameConfig.ConfigDict[kvpair.Key] = kvpair.Value;
                            }
                        });
                    }

                    else if (action == GameRoomMessageDefines.ROOM_GAMESTART.Action)
                    {
                        if (CurrentGame != null)
                        {
                            CurrentGame.DisposeGame();
                        }

                        _dispatcher.BeginInvoke(delegate
                        {
                            CurrentGame = GameImporter.CreateGame(
                                _curgameroom.GameConfig.GameIdentityName,
                                this.CurrentGameRoom,
                                this.CurrentGameRoom.Players.FirstOrDefault(p => p.PlayerID == LoginPlayer.PlayerID),
                                this.GameServiceClientProxy);

                            // need refactor
                            //var gamewindow = new OnlineGameClient.View.GameWindow();
                            //gamewindow.DataContext = this;

                            //gamewindow.Show();
                            //CurrentGame.GameStart();

                            SwitchView("GameView");
                            CurrentGame.GameStart();


                            this.CurrentGameRoom.GameState = GameState.Running;
                        });
                    }
                    else if (action == GameRoomMessageDefines.ROOM_UPDATE.Action)
                    {
                        var gameroom = (GameRoom)GameRoomMessageDefines.ROOM_UPDATE.GetMessage(e.Message).MessageBodyContent;
                        _dispatcher.BeginInvoke(delegate
                        {
                            var gameroom2 = FindGameRoom(gameroom.GameId);

                            // update state
                            if (gameroom2 != null)
                            {
                                gameroom2.GameState = gameroom.GameState;
                                AttendGameRoomCommand.RaiseCanExecuteChanged();
                            }

                            if (this.CurrentGameRoom != null &&
                                gameroom.GameId == this.CurrentGameRoom.GameId)
                                this.CurrentGameRoom.GameState = gameroom.GameState;
                        });
                    }
                    else if (action == GameRoomMessageDefines.ROOM_NEW.Action)
                    {
                        var gameroom = (GameRoom)GameRoomMessageDefines.ROOM_NEW.GetMessage(e.Message).MessageBodyContent;
                        _dispatcher.BeginInvoke(delegate
                        {
                            ((List<GameRoom>)this.GameRoomList.SourceCollection).Add(gameroom);
                            this.GameRoomList.Refresh();
                        });
                    }
                    else if (action == GameRoomMessageDefines.ROOM_DELETE.Action)
                    {
                        var gameid = (Guid)GameRoomMessageDefines.ROOM_DELETE.GetMessage(e.Message).MessageBodyContent;
                        _dispatcher.BeginInvoke(delegate
                        {
                            var gameroom = FindGameRoom(gameid);
                            ((List<GameRoom>)this.GameRoomList.SourceCollection).Remove(gameroom);
                            this.GameRoomList.Refresh();
                        });
                    }
                    else
                    {
                        throw new Exception("cannot resolve message, scope: " + scope);
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
                //MessageBox.Show(ex.Message);
            }
        }

        GameRoom FindGameRoom(Guid gameid)
        {
            foreach (GameRoom gm in this.GameRoomList)
                if (gm.GameId == gameid)
                    return gm;
            return null;
        }

        #endregion

        #region command
        public DelegateCommand<GameConfig> UpdateConfigCommand { private set; get; }
        public DelegateCommand<object> GetGameRoomsCommand { private set; get; }
        public DelegateCommand<object> CreateGameRoomCommand { private set; get; }
        public DelegateCommand<GameRoom> AttendGameRoomCommand { private set; get; }
        public DelegateCommand<object> LeaveGameRoomCommand { private set; get; }
        public DelegateCommand<object> StartGameCommand { private set; get; }
        public DelegateCommand<Player> PlayerLoginCommand { private set; get; }
        public DelegateCommand<object> LoadGameInfoListCommand { private set; get; }
        public DelegateCommand<object> BackToGameRoomCommand { private set; get; }

        void SwitchView(string view)
        {
            _dispatcher.BeginInvoke(delegate
            {
                var mainview = App.Current.RootVisual as OnlineGameClient.View.MainView;
                mainview.SwithView(view);
            });
        }

        void SetCurrentGame(GameRoom gameroom)
        {
            if (this.GameInfoList != null)
                gameroom.GameInfo = this.GameInfoList.First(gi => gi.IdentityName == gameroom.GameConfig.GameIdentityName);
            this.CurrentGameRoom = gameroom;
        }

        void InitializeGameRoomSource(IList<GameRoom> gameroomlist)
        {
            this.GameRoomList = new PagedCollectionView(gameroomlist);
            this.GameRoomList.SortDescriptions.Add(new SortDescription("GameConfig.GameIdentityName", ListSortDirection.Ascending));
            //this.GameRoomList.GroupDescriptions.Add(new PropertyGroupDescription("GameConfig.GameIdentityName"));
        }

        void InitializeCommand()
        {
            UpdateConfigCommand = new DelegateCommand<GameConfig>(
                gc => GameServiceClientProxy.GameService.BeginUpdateGameConfig((GameConfig)gc,
                    new AsyncCallback((ar) =>
                        {
                            GameServiceClientProxy.GameService.EndUpdateGameConfig(ar);
                        }),
                        null),
                gc => true);

            GetGameRoomsCommand = new DelegateCommand<object>(
                (o) =>
                {
                    GameServiceClientProxy.GameService.BeginGetGameRooms(
                        new AsyncCallback((ar) =>
                            {
                                _dispatcher.BeginInvoke(delegate
                                {
                                    InitializeGameRoomSource(GameServiceClientProxy.GameService.EndGetGameRooms(ar));
                                });
                            }),
                            null);
                },
                (o) => true
            );

            AttendGameRoomCommand = new DelegateCommand<GameRoom>(
                (o) =>
                {
                    var selectedroom = o as GameRoom;
                    lock (currentgameroomlock)
                    {
                        GameServiceClientProxy.GameService.BeginAttendGameRoom(
                            selectedroom.GameId,
                            new AsyncCallback((ar) =>
                                {
                                    var result = GameServiceClientProxy.GameService.EndAttendGameRoom(ar);
                                    if (result != null)
                                    {
                                        SetCurrentGame(result);
                                        SwitchView("GameRoomDetailView");
                                        DownloadGameXap();
                                    }
                                    else
                                    {
                                        // dialogservice notify user.
                                    }
                                }),
                                null);
                    }
                },
                (o) => (
                    this.CurrentGameRoom == null &&
                    o != null &&
                    ((GameRoom)o).GameState == GameState.Begin));

            LeaveGameRoomCommand = new DelegateCommand<object>(
                (o) =>
                {
                    GameServiceClientProxy.GameService.BeginLeaveGameRoom(
                        new AsyncCallback((ar) =>
                            {
                                var result =
                                    GameServiceClientProxy.GameService.EndLeaveGameRoom(ar);
                                if (result.IsSuccess)
                                {
                                    if (this.CurrentGame != null)
                                    {
                                        this.CurrentGame.DisposeGame();
                                        this.CurrentGame = null;
                                    }
                                    this.CurrentGameRoom = null;
                                    SwitchView("GameRoomsView");
                                }
                                else
                                {
                                    // dialogservice notify user.
                                }
                            }),
                            null);
                },
                (o) => (
                    this.CurrentGameRoom != null &&
                    this.CurrentGameRoom.GameState == GameState.Begin));

            // parameter currentgameroom
            StartGameCommand = new DelegateCommand<object>(
                (o) =>
                {
                    this.GameServiceClientProxy.GameService.BeginGameStart(
                        new AsyncCallback((ar) =>
                            {
                                var result = GameServiceClientProxy.GameService.EndGameStart(ar);

                                if (result.IsSuccess)
                                {
                                    //..
                                }
                            }),
                            null);
                },
                (o) => this.CurrentGameRoom != null &&
                    this.LoginPlayer.PlayerID == this.CurrentGameRoom.Players[0].PlayerID);

            BackToGameRoomCommand = new DelegateCommand<object>(
                (o) =>
                {
                    SwitchView("GameRoomDetailView");
                },
                (o) => this.CurrentGameRoom != null && this.CurrentGameRoom.GameState != GameState.Running);

            InitializePlayerLoginCommand();
            InitializeLoadGameInfoListCommand();
            InitializeCreateGameRoomCommand();
        }

        void DownloadGameXap()
        {
            _dispatcher.BeginInvoke(delegate
            {
                this.LoginPlayer.PlayerState = PlayerState.Initializing;
                _deploymentcatalogservice.AddXap(
                    new Uri(GameInfoList.First(g => g.IdentityName
                        == this.CurrentGameRoom.GameConfig.GameIdentityName).GameClientComponentUri+ "clientgame.xap"),
                        (ae) =>
                        {
                            // check if has error
                            if (ae.Error != null)
                                throw ae.Error;

                            this.LoginPlayer.PlayerState = PlayerState.Ready;

                            // haven't handle back event.
                            this.GameServiceClientProxy.GameService.BeginUpdatePlayerState(
                                PlayerState.Ready,
                                null,
                                null);
                        },
                        (pe) =>
                        {
                            this.DownloadProgress = pe.ProgressPercentage;
                        });
            });
        }

        void InitializePlayerLoginCommand()
        {
            PlayerLoginCommand = new DelegateCommand<Player>(
                (o) =>
                {
                    //show dialog
                    this.GameServiceClientProxy.GameService.BeginPlayerLogin(
                        "?",
                        new AsyncCallback((ar) =>
                        {
                            this.LoginPlayer =
                                GameServiceClientProxy.GameService.EndPlayerLogin(ar);
                            LoadGameInfoListCommand.Execute(null);
                        }),
                            null);
                },
                (o) => o == null
            );
        }

        void InitializeLoadGameInfoListCommand()
        {
            LoadGameInfoListCommand = new DelegateCommand<object>(
                (o) =>
                {
                    this.GameServiceClientProxy.GameService.BeginGetGameList(
                        new AsyncCallback((ar) =>
                            {
                                this.GameInfoList =
                                    GameServiceClientProxy.GameService.EndGetGameList(ar);
                            }),
                            null);
                });
        }

        void InitializeCreateGameRoomCommand()
        {
            CreateGameRoomCommand = new DelegateCommand<object>(
                (o) =>
                {
                    ////////////////////////////////////////////////////////////////////////
                    // need refactor here
                    var configwindow = new OnlineGameClient.View.GameConfigView();
                    var configvm = new GameConfigViewModel(this.GameInfoList);

                    _modeldialgservice.ShowDialog<GameConfigViewModel>(configwindow, configvm,
                        (c) =>
                        {
                            var result = configwindow.DialogResult;
                            if (result.HasValue && result.Value)
                            {
                                ////////////////////////////////////////////////////////////
                                // lock is invalid in async call pattern.
                                // need redesign
                                lock (currentgameroomlock)
                                {
                                    GameServiceClientProxy.GameService.BeginCreateGameRoom(
                                        configvm.GameConfig,
                                        new AsyncCallback((ar) =>
                                        {
                                            var gameroom =
                                                GameServiceClientProxy.GameService.EndCreateGameRoom(ar);
                                            if (gameroom != null)
                                            {
                                                SetCurrentGame(gameroom);
                                                SwitchView("GameRoomDetailView");
                                                DownloadGameXap();
                                            }
                                            else
                                            {
                                                // dialogservice notify user.
                                            }
                                        }),
                                        null);
                                }
                            }
                        }
                    );
                },
                (o) => this.CurrentGameRoom == null);
        }

        #endregion
    }
}
